package net.realsee.service.wcs.intf.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import net.realsee.common.config.AppConfig;
import net.realsee.common.enums.wcs.intf.InterfaceResultCodeEnum;
import net.realsee.common.exception.ServiceException;
import net.realsee.common.utils.InterfaceUtils;
import net.realsee.common.utils.StringUtils;
import net.realsee.domain.wcs.intf.WcsExtApp;
import net.realsee.domain.wcs.intf.WcsIntf;
import net.realsee.domain.wcs.intf.WcsIntfOutlog;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.domain.wcs.intf.bo.WcsIntfOutlogBo;
import net.realsee.domain.wcs.intf.vo.WcsIntfDataVo;
import net.realsee.domain.wcs.intf.vo.WcsIntfOutlogVo;
import net.realsee.framework.core.domain.PageQuery;
import net.realsee.framework.core.page.TableDataInfo;
import net.realsee.mapper.wcs.intf.WcsIntfOutlogMapper;
import net.realsee.service.wcs.intf.IWcsExtAppService;
import net.realsee.service.wcs.intf.IWcsIntfDataService;
import net.realsee.service.wcs.intf.IWcsIntfOutlogService;
import net.realsee.service.wcs.intf.IWcsIntfService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Outbound接口日志Service业务层处理
 *
 * @author realsee
 * @date 2023-08-11
 */
@RequiredArgsConstructor
@Service
public class WcsIntfOutlogServiceImpl extends ServiceImpl<WcsIntfOutlogMapper, WcsIntfOutlog> implements IWcsIntfOutlogService {

    private final WcsIntfOutlogMapper baseMapper;
    private final IWcsIntfDataService dataService;
    private final IWcsIntfService intfService;
    private final IWcsExtAppService extAppService;
    private final AppConfig appConfig;

    /**
     * 查询Outbound接口日志
     */
    @Override
    public WcsIntfOutlogVo selectVoById(Long id) {
        WcsIntfOutlogVo result = baseMapper.selectVoById(id);
        if (ObjectUtil.isNotNull(result) && ObjectUtil.isNotNull(result.getOutputDataId()) && 0L != result.getOutputDataId()) {
            WcsIntfDataVo outputDataVo = dataService.selectVoById(result.getOutputDataId());
            if (ObjectUtil.isNotNull(outputDataVo) && StrUtil.isNotBlank(outputDataVo.getContent())) {
                result.setOutputData(StringUtils.beautify(outputDataVo.getContent()));
            }
        }
        if (ObjectUtil.isNotNull(result) && ObjectUtil.isNotNull(result.getFeedbackDataId()) && 0L != result.getFeedbackDataId()) {
            WcsIntfDataVo feedbackDataVo = dataService.selectVoById(result.getFeedbackDataId());
            if (ObjectUtil.isNotNull(feedbackDataVo) && StrUtil.isNotBlank(feedbackDataVo.getContent())) {
                result.setFeedbackData(StringUtils.beautify(feedbackDataVo.getContent()));
            }
        }
        return result;
    }

    /**
     * 查询Outbound接口日志列表
     */
    @Override
    public TableDataInfo<WcsIntfOutlogVo> selectVoPage(WcsIntfOutlogBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WcsIntfOutlog> lqw = buildVoQueryWrapper(bo);
        Page<WcsIntfOutlogVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询Outbound接口日志列表
     */
    @Override
    public List<WcsIntfOutlogVo> selectVoList(WcsIntfOutlogBo bo) {
        LambdaQueryWrapper<WcsIntfOutlog> lqw = buildVoQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WcsIntfOutlog> buildVoQueryWrapper(WcsIntfOutlogBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<WcsIntfOutlog> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, WcsIntfOutlog::getId, bo.getId());
        lqw.eq(StringUtils.isNotBlank(bo.getTaskId()), WcsIntfOutlog::getTaskId, bo.getTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppCode()), WcsIntfOutlog::getAppCode, bo.getAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getTaskAppCode()), WcsIntfOutlog::getTaskAppCode, bo.getTaskAppCode());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceCode()), WcsIntfOutlog::getInterfaceCode, bo.getInterfaceCode());
        lqw.like(StringUtils.isNotBlank(bo.getInterfaceName()), WcsIntfOutlog::getInterfaceName, bo.getInterfaceName());
        lqw.eq(StringUtils.isNotBlank(bo.getInterfaceType()), WcsIntfOutlog::getInterfaceType, bo.getInterfaceType());
        lqw.eq(bo.getCallOn() != null, WcsIntfOutlog::getCallOn, bo.getCallOn());
        lqw.eq(bo.getCompletionOn() != null, WcsIntfOutlog::getCompletionOn, bo.getCompletionOn());
        lqw.eq(StringUtils.isNotBlank(bo.getHostAddr()), WcsIntfOutlog::getHostAddr, bo.getHostAddr());
        lqw.eq(bo.getCommPort() != null, WcsIntfOutlog::getCommPort, bo.getCommPort());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), WcsIntfOutlog::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getCallbackUrl()), WcsIntfOutlog::getCallbackUrl, bo.getCallbackUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getMethod()), WcsIntfOutlog::getMethod, bo.getMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getHeader()), WcsIntfOutlog::getHeader, bo.getHeader());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthType()), WcsIntfOutlog::getAuthType, bo.getAuthType());
        lqw.eq(StringUtils.isNotBlank(bo.getAuthData()), WcsIntfOutlog::getAuthData, bo.getAuthData());
        lqw.eq(bo.getHttpCode() != null, WcsIntfOutlog::getHttpCode, bo.getHttpCode());
        lqw.eq(StringUtils.isNotBlank(bo.getResultCode()), WcsIntfOutlog::getResultCode, bo.getResultCode());
        lqw.eq(StringUtils.isNotBlank(bo.getMessage()), WcsIntfOutlog::getMessage, bo.getMessage());
        lqw.eq(bo.getStatus() != null, WcsIntfOutlog::getStatus, bo.getStatus());
        lqw.eq(bo.getMaxRetry() != null, WcsIntfOutlog::getMaxRetry, bo.getMaxRetry());
        lqw.eq(bo.getRetryCounter() != null, WcsIntfOutlog::getRetryCounter, bo.getRetryCounter());
        lqw.eq(StringUtils.isNotBlank(bo.getKeyword1()), WcsIntfOutlog::getKeyword1, bo.getKeyword1());
        lqw.eq(StringUtils.isNotBlank(bo.getKeyword2()), WcsIntfOutlog::getKeyword2, bo.getKeyword2());
        lqw.eq(StringUtils.isNotBlank(bo.getParTaskId()), WcsIntfOutlog::getParTaskId, bo.getParTaskId());
        lqw.eq(StringUtils.isNotBlank(bo.getInnerRequest()), WcsIntfOutlog::getInnerRequest, bo.getInnerRequest());
        lqw.eq(StringUtils.isNotBlank(bo.getOutputData()), WcsIntfOutlog::getOutputData, bo.getOutputData());
        lqw.eq(StringUtils.isNotBlank(bo.getFeedbackData()), WcsIntfOutlog::getFeedbackData, bo.getFeedbackData());
        lqw.eq(bo.getOutputDataId() != null, WcsIntfOutlog::getOutputDataId, bo.getOutputDataId());
        lqw.eq(bo.getFeedbackDataId() != null, WcsIntfOutlog::getFeedbackDataId, bo.getFeedbackDataId());
        lqw.eq(bo.getOrderHeaderId() != null, WcsIntfOutlog::getOrderHeaderId, bo.getOrderHeaderId());
        lqw.eq(bo.getOrderDetailId() != null, WcsIntfOutlog::getOrderDetailId, bo.getOrderDetailId());
        lqw.eq(StringUtils.isNotBlank(bo.getFuid()), WcsIntfOutlog::getFuid, bo.getFuid());
        lqw.eq(bo.getTextId() != null, WcsIntfOutlog::getTextId, bo.getTextId());
        lqw.eq(bo.getRefrenceId() != null, WcsIntfOutlog::getRefrenceId, bo.getRefrenceId());
        lqw.eq(bo.getUnitId() != null, WcsIntfOutlog::getUnitId, bo.getUnitId());
        lqw.eq(bo.getDelFlag() != null, WcsIntfOutlog::getDelFlag, bo.getDelFlag());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), WcsIntfOutlog::getCreateBy, bo.getCreateBy());
        lqw.eq(bo.getCreateTime() != null, WcsIntfOutlog::getCreateTime, bo.getCreateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), WcsIntfOutlog::getUpdateBy, bo.getUpdateBy());
        lqw.eq(bo.getUpdateTime() != null, WcsIntfOutlog::getUpdateTime, bo.getUpdateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getDeleteBy()), WcsIntfOutlog::getDeleteBy, bo.getDeleteBy());
        lqw.eq(bo.getDeleteTime() != null, WcsIntfOutlog::getDeleteTime, bo.getDeleteTime());
        lqw.eq(bo.getRowVersionStamp() != null, WcsIntfOutlog::getRowVersionStamp, bo.getRowVersionStamp());
        lqw.eq(StringUtils.isNotBlank(bo.getRemark()), WcsIntfOutlog::getRemark, bo.getRemark());
        lqw.orderByDesc(WcsIntfOutlog::getId);
        return lqw;
    }

    /**
     * 新增Outbound接口日志
     */
    @Override
    public Boolean insertByBo(WcsIntfOutlogBo bo) {
        WcsIntfOutlog add = BeanUtil.toBean(bo, WcsIntfOutlog.class);
        add.setFuid(IdUtil.fastUUID());
        return this.save(add);
    }

    /**
     * 修改Outbound接口日志
     */
    @Override
    public Boolean updateByBo(WcsIntfOutlogBo bo) {
        WcsIntfOutlog update = BeanUtil.toBean(bo, WcsIntfOutlog.class);
        return this.updateById(update);
    }

    /**
     * 批量删除Outbound接口日志
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        return this.removeBatchByIds(ids);
    }

    /**
     * 新增Outbound接口日志
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public WcsIntfOutlog insertByRequest(InterfaceRequest interfaceRequest) {
        if (ObjectUtil.isNull(interfaceRequest)) {
            // 不需要记录日志
            return null;
        }

        WcsIntf intf = intfService.getByInterfaceCode(interfaceRequest.getInterfaceCode().getCode());
        if (ObjectUtil.isNull(intf)) {
            throw new ServiceException(StrUtil.format("接口代码错误, code={}", interfaceRequest.getInterfaceCode().getCode()));
        }
        if (!isRecord(interfaceRequest, intf)) {
            // 不需要更新日志
            return null;
        }

        WcsExtApp extApp = extAppService.getByAppCode(intf.getAppCode());
        if (ObjectUtil.isNull(extApp)) {
            throw new ServiceException(StrUtil.format("系统定义对象错误, appCode={}", intf.getAppCode()));
        }
        WcsIntfOutlog outlog = new WcsIntfOutlog();
        outlog.setTaskId(interfaceRequest.getTaskId());
        outlog.setAppCode(intf.getAppCode());
        outlog.setTaskAppCode(appConfig.getName());
        outlog.setInterfaceCode(intf.getInterfaceCode());
        outlog.setInterfaceName(intf.getInterfaceName());
        outlog.setInterfaceType(intf.getInterfaceType());
        outlog.setCallOn(new Date());
        outlog.setHostAddr(extApp.getHostAddr());
        outlog.setCommPort(extApp.getHttpInterfacePort());
        outlog.setUrl(intf.getUrl());
        outlog.setMethod(intf.getMethod());
        outlog.setHeader(JSONUtil.toJsonStr(interfaceRequest.getHeaderObj()));
        outlog.setAuthType(intf.getAuthType());
        outlog.setAuthData(intf.getAuthData());
        outlog.setMaxRetry(intf.getMaxRetry());
        outlog.setParTaskId(interfaceRequest.getParTaskId());
        outlog.setFuid(IdUtil.fastUUID());
        if (StrUtil.isNotBlank(interfaceRequest.getBodyStr())) {
            outlog.setOutputDataId(dataService.insertByContent(interfaceRequest.getBodyStr()));
        } else if (ObjectUtil.isNotNull(interfaceRequest.getParamObj())) {
            outlog.setOutputDataId(dataService.insertByContent(JSONUtil.toJsonStr(interfaceRequest.getParamObj())));
        }
        outlog.setKeyword1(interfaceRequest.getKeyword1());
        outlog.setKeyword2(interfaceRequest.getKeyword2());
        outlog.setInnerRequest(JSONUtil.toJsonStr(interfaceRequest));
        this.save(outlog);
        return outlog;
    }

    /**
     * 修改Outbound接口日志
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public WcsIntfOutlog updateByResponse(InterfaceRequest interfaceRequest, HttpResponse httpResponse, WcsIntfOutlog outlog, Long retryCounter) {
        if (ObjectUtil.isNull(interfaceRequest) || ObjectUtil.isNull(outlog)) {
            // 不需要更新日志
            return null;
        }

        WcsIntf intf = intfService.getByInterfaceCode(interfaceRequest.getInterfaceCode().getCode());
        if (ObjectUtil.isNull(intf)) {
            throw new ServiceException(StrUtil.format("接口代码错误, code={}", interfaceRequest.getInterfaceCode().getCode()));
        }
        if (!isRecord(interfaceRequest, intf)) {
            // 不需要更新日志
            return null;
        }

        outlog.setCompletionOn(new Date());
        outlog.setRetryCounter(retryCounter);
        outlog.setStatus(InterfaceResultCodeEnum.FAILED.getCode());
        if (ObjectUtil.isNotNull(httpResponse)) {
            if (httpResponse.isOk()) {
                outlog.setStatus(InterfaceResultCodeEnum.SUCCESS.getCode());
            }
            outlog.setHttpCode((long) httpResponse.getStatus());
            outlog.setFeedbackDataId(dataService.insertByContent(httpResponse.body()));
            //outlog.setResultCode();
            //outlog.setMessage();
        }
        updateById(outlog);
        return outlog;
    }

    /**
     * 修改Outbound接口日志
     */
    @Override
    public WcsIntfOutlog updateByResponse(InterfaceResponse response) {
        return null;
    }

    /**
     * 接口重试
     */
    @Override
    public Boolean retry(WcsIntfOutlogBo bo) {
        if (ObjectUtil.isNull(bo) || ObjectUtil.isNull(bo.getId())) {
            return false;
        }
        WcsIntfOutlog wcsIntfOutlog = this.getById(bo.getId());
        if (ObjectUtil.isNull(wcsIntfOutlog) || StrUtil.isBlank(wcsIntfOutlog.getInnerRequest())) {
            return false;
        }
        InterfaceRequest interfaceRequest = JSONUtil.toBean(wcsIntfOutlog.getInnerRequest(), InterfaceRequest.class);
        if (ObjectUtil.isNull(interfaceRequest)) {
            return false;
        }
        interfaceRequest.setParTaskId(bo.getId().toString());
        InterfaceUtils.callByHttp(interfaceRequest);
        return true;
    }

    private Boolean isRecord(InterfaceRequest interfaceRequest, WcsIntf intf) {
        boolean result = false;
        if (ObjectUtil.isNotNull(interfaceRequest.getIsRecord())) {
            result = interfaceRequest.getIsRecord();
        } else {
            // 传入的优先级高
            if (StrUtil.isNotBlank(intf.getIsRecord())) {
                result = intf.isRecord();
            }
        }
        return result;
    }
}
